﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Go;
using GoRpc;

namespace GoRpcTest
{
    class Program
    {
        class remote_obj
        {
            public Rpc.Session sess;
            int value1 = 0;
            int value2 = 0;

            async Task<int> add()
            {
                await generator.sleep(1000);
                return value1 + value2;
            }

            async Task<int> sub()
            {
                await generator.sleep(1000);
                return value1 - value2;
            }

            async Task<int> recv(long msgLen)
            {
                var recvs = Rpc.Session.OverRecv();
                byte[] buffer = new byte[msgLen];
                int offset = 0;
                for (int i = 0; i < recvs.Length; i++)
                {
                    while (!recvs[i].Full && !recvs[i].Cancel)
                    {
                        await sess.NamedRecv(recvs[i], buffer, (int)(offset + recvs[i].Offset), (int)(recvs[i].Length - recvs[i].Offset));
                    }
                    offset += (int)recvs[i].Length;
                }
                string msg = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
                Console.WriteLine(msg);
                return recvs.Length;
            }
        }

        static void Main(string[] args)
        {
            work_service work = new work_service();
            work_strand strand = new work_strand(work);
            generator.go(strand, async delegate ()
            {
                Rpc.Methods methods = new Rpc.Methods();
                remote_obj obj = new remote_obj();
                methods.BindObj("remote", obj);
                socket_tcp.acceptor accep = new socket_tcp.acceptor();
                if (!accep.bind("127.0.0.1", 2001))
                {
                    return;
                }
                socket_tcp socket = new socket_tcp();
                if (!await accep.accept(socket))
                {
                    return;
                }
                accep.close();
                generator.children sessions = new generator.children();
                generator.children sessions2 = new generator.children();
                Rpc.Session sess = new Rpc.Session(strand, socket, methods);
                sessions.go(async delegate ()
                {
                    obj.sess = sess;
                    sess.Start();
                    await sess.Wait();
                    Console.WriteLine($"Server.Disconnected {sess.Code}");
                });
                sessions2.go(async delegate ()
                {
                    await sess.Call("remote.set@value1", 123);
                    await sess.Call("remote.set@value2", 456);
                    Rpc.Result<int> r1 = await sess.Call<int>("remote.add");
                    Rpc.Result<int> r2 = await sess.Call<int>("remote.sub");
                    Console.WriteLine($"Server.Add {r1.result}");
                    Console.WriteLine($"Server.Sub {r2.result}");
                });
                sessions2.go(async delegate ()
                {
                    using (FileStream file = File.OpenRead("..\\..\\Program.cs"))
                    {
                        sess.OverNamedSend(file.SafeFileHandle, 0, file.Length);
                        Rpc.Result<int> r3 = await sess.Call<int>("remote.recv", file.Length);
                        Console.WriteLine($"Server.Recv1 {r3.result}");
                    }
                });
                sessions2.go(async delegate ()
                {
                    using (FileStream file = File.OpenRead("..\\..\\App.config"))
                    {
                        long len1 = file.Length / 2;
                        long len2 = file.Length - len1;
                        sess.OverNamedSend(file.SafeFileHandle, 0, len1, 32);
                        sess.OverNamedSend(file.SafeFileHandle, len1, len2, 32);
                        Rpc.Result<int> r3 = await sess.Call<int>("remote.recv", file.Length);
                        Console.WriteLine($"Server.Recv2 {r3.result}");
                    }
                });
                sessions2.go(async delegate ()
                {
                    using (FileStream file = File.OpenRead("..\\..\\App.config"))
                    {
                        long len1 = file.Length / 2;
                        long len2 = file.Length - len1;
                        Rpc.SendDoubleBuffer dbuff = new Rpc.SendDoubleBuffer(32);
                        dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, len1)));
                        sess.OverNamedSend(len1, async delegate (Rpc.SendName name, long offset, long count)
                        {
                            await dbuff.Exchange();
                            if (dbuff.Error)
                            {
                                return;
                            }
                            ArraySegment<byte> data = dbuff.SendData;
                            if (count - data.Count > 0)
                            {
                                dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, count - data.Count)));
                            }
                            await sess.NamedSend(name, data);
                        });
                        sess.OverNamedSend(len2, delegate (Rpc.SendName name, long offset, long count)
                        {
                            return sess.NamedSend(name, file.SafeFileHandle, len1 + offset, (int)Math.Min(32, count));
                        });
                        Rpc.Result<int> r3 = await sess.Call<int>("remote.recv", file.Length);
                        Console.WriteLine($"Server.Recv3 {r3.result}");
                    }
                });
                sessions.go(async delegate ()
                {
                    await sess.Call("Callback", sess.OverCallback(async delegate (object[] ps)
                    {
                        int a = (int)ps[0];
                        int b = (int)ps[1];
                        await generator.sleep(1000);
                        return a + b;
                    }));
                });
                await sessions2.wait_all();
                sess.Close();
            });
            generator.go(strand, async delegate ()
            {
                await generator.sleep(100);
                Rpc.Methods methods = new Rpc.Methods();
                remote_obj obj = new remote_obj();
                methods.BindObj("remote", obj);
                methods.Bind("Callback", async delegate (object[] ps)
                {
                    string callback = (string)ps[0];
                    Rpc.Result<int> r1 = await Rpc.Session.Self.Call<int>(callback, 123, 456);
                    Console.WriteLine($"Client.Callback {r1.result}");
                });
                socket_tcp socket = new socket_tcp();
                if (await socket.connect("127.0.0.1", 2001))
                {
                    generator.children sessions = new generator.children();
                    Rpc.Session sess = new Rpc.Session(strand, socket, methods);
                    sessions.go(async delegate ()
                    {
                        await sess.Call("remote.set@value1", 456);
                        await sess.Call("remote.set@value2", 123);
                        Rpc.Result<int> r1 = await sess.Call<int>("remote.add");
                        Rpc.Result<int> r2 = await sess.Call<int>("remote.sub");
                        Console.WriteLine($"Client.Add {r1.result}");
                        Console.WriteLine($"Client.Sub {r2.result}");
                    });
                    obj.sess = sess;
                    sess.Start();
                    await sess.Wait();
                    Console.WriteLine($"Client.Disconnected {sess.Code}");
                    await sessions.stop();
                }
                socket.close();
            });
            work.run();
            Console.WriteLine("work done");
            Console.ReadKey();
        }
    }
}
